package swing.ui;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.Icon;
import javax.swing.tree.DefaultMutableTreeNode;



@SuppressWarnings("serial")
public class SelectableTreeNode extends DefaultMutableTreeNode {

	public final static int SINGLE_SELECTION = 0;

	public final static int DIG_IN_SELECTION = 1;

	private int state;
	private int selectionMode;
	private String name;
	private boolean orderable;


	private Icon[] icons;

	/**
	 * Creates a tree node
	 * 
	 * @param userObject 		object to be selected
	 * @param name				name that describe the userObject
	 * @param openIcon			icon for opened node
	 * @param closeIcon			icon for closed node
	 * @param allowsChildren	allow having children or not
	 * @param isOrderable		by ordering, is the node included.
	 * @param state				initial state
	 */
	public SelectableTreeNode(Object userObject, String name, Icon openIcon,
			Icon closeIcon, boolean allowsChildren, boolean orderable, int state) {
		super(userObject, allowsChildren);
		this.state = state;
		this.name = name;
		this.icons = new Icon[2];
		this.icons[0] = openIcon;
		this.icons[1] = closeIcon;
		this.orderable = orderable;
		setSelectionMode(SINGLE_SELECTION);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode(Object userObject, String name, Icon openIcon,
			Icon closeIcon, boolean allowsChildren, int state) {
		this(userObject, name, openIcon, closeIcon, allowsChildren,
				!allowsChildren, state);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode(Object userObject, String name, Icon icon,
			boolean allowsChildren, boolean selected) {
		this(userObject, name, icon, icon, allowsChildren,
				selected ? TristateCheckBox.SELECTED
						: TristateCheckBox.NOT_SELECTED);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode(Object userObject, String name,
			boolean allowsChildren, boolean selected) {
		this(userObject, name, null, allowsChildren, selected);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode(Object userObject, boolean allowsChildren,
			boolean selected) {
		this(userObject, userObject.toString(), null, allowsChildren, selected);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode(Object userObject, boolean allowsChildren) {
		this(userObject, allowsChildren, false);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode(Object userObject) {
		this(userObject, true, false);
	}

	/**
	 * @see #SelectableTreeNode(Object, String, Icon, Icon, boolean, int)
	 */
	public SelectableTreeNode() {
		this(new Object());
	}

	/**
	 * Sets the selection mode
	 * 
	 * @param mode
	 * 		SINGLE_SELECTION or DIG_IN_SELECTION
	 */
	public void setSelectionMode(int mode) {
		selectionMode = mode;
	}

	/**
	 * Gets the selection mode
	 * 
	 * @return SINGLE_SELECTION or DIG_IN_SELECTION
	 * @see #setSelectionMode(int)
	 */
	public int getSelectionMode() {
		return selectionMode;
	}

	/**
	 * Gets the state of tree node
	 * 
	 * @return the state
	 * @see #setSelected(boolean)
	 * @see #getSelected()
	 */
	public int getState() {
		return state;
	}

	/**
	 * Gets the state of the node
	 * 
	 * @return the state
	 */
	public boolean getSelected() {
		return state == TristateCheckBox.SELECTED;
	}

	/**
	 * Sets the state of node
	 * 
	 * @param b new state
	 */
	public void setSelected(boolean b) {
		setState(b ? TristateCheckBox.SELECTED : TristateCheckBox.NOT_SELECTED);
	}

	/**
	 * Sets the property of orderable
	 * a node is orderable if it can be ordered in ordering tree control
	 * 
	 * @param b
	 */
	public void setOrderable(boolean b) {
		this.orderable = b;
	}

	/**
	 * @see #setOrderable(boolean)
	 */
	public boolean getOrderable() {
		return this.orderable;
	}

	/**
	 * The returned text is showed at the label of tree node. 
	 * cooperation with SelectableTreeNodeRenderer
	 */
	public String toString() {
		return name;
	}

	/**
	 * Cooperation with SelectableTreeNodeRenderer
	 * @param expand
	 * @return
	 */
	public Icon getIcon(boolean expanded) {
		return icons[expanded ? 0 : 1];
	}

	/**
	 * Sets the name of node
	 * 
	 * @param name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Sets the icons of node
	 * 
	 * @param icon
	 */
	public void setIcon(Icon icon) {
		this.icons[0] = icon;
		this.icons[1] = icon;
	}

	/**
	 * Set the icons of node
	 * 
	 * @param openIcon
	 * @param closeIcon
	 */
	public void setIcon(Icon openIcon, Icon closeIcon) {
		this.icons[0] = openIcon;
		this.icons[1] = closeIcon;
	}

	/**
	 * refreshs the states of THIS NODE AND ITS CHILDREN to keep consistent 
	 */
	public void refresh() {
		if (children != null) {
			Iterator it = children.iterator();
			while (it.hasNext()) {
				SelectableTreeNode child = (SelectableTreeNode) it.next();
				child.refresh();
			}
			refreshState();
		}
	}

	/**
	 * Get the orderable children of this node
	 * 
	 * @return list of orderable nodes
	 */
	@SuppressWarnings("unchecked")
	public List getOrderableChildren() {
		List result = new ArrayList();
		if (this.getOrderable()) {
			result.add(this);
		} else if (children != null) {
			Iterator it = children.iterator();
			while (it.hasNext()) {
				SelectableTreeNode child = (SelectableTreeNode) it.next();
				result.addAll(child.getOrderableChildren());
			}
		} else {
			SelectableTreeNode parent = (SelectableTreeNode) this.getParent();
			while (parent != null) {
				if (parent.getOrderable()) {
					result.add(parent);
					break;
				}
				parent = (SelectableTreeNode) parent.getParent();
			}
		}

		return result;
	}

	private void setState(int state) {
		this.state = state;

		if (selectionMode == DIG_IN_SELECTION) {
			setChildrenState(this, state);
		}
		setParentsState(this);
	}

	private void setParentsState(SelectableTreeNode node) {
		SelectableTreeNode parent = (SelectableTreeNode) node.getParent();
		if (parent != null) {
			parent.refreshState();
			setParentsState(parent);
		}
	}

	private void setChildrenState(SelectableTreeNode father, int state) {
		if (father.children == null)
			return;

		Iterator it = father.children.iterator();
		while (it.hasNext()) {
			SelectableTreeNode node = (SelectableTreeNode) it.next();
			node.state = state;
			setChildrenState(node, state);
		}
	}

	/**
	 * refresh ONLY the state of this node according to direct children
	 */
	private void refreshState() {
//		if (children != null) {
//			Iterator it = children.iterator();
//			int count = children.size();
//			int selected = 0;
//			while (it.hasNext()) {
//				SelectableTreeNode child = (SelectableTreeNode) it.next();
//				if (child.state == TristateCheckBox.HALF_SELECTED) {
//					this.state = TristateCheckBox.HALF_SELECTED;
//					return;
//				}
//				if (child.getSelected())
//					selected++;
//			}
//			if (selected == 0)
//				this.state = TristateCheckBox.NOT_SELECTED;
//			else if (selected < count)
//				this.state = TristateCheckBox.HALF_SELECTED;
//			else
//				this.state = TristateCheckBox.SELECTED;
//		}
	}


}
